#include "Smoke3DFlatWin.h"
#include <windows.h>
#include <iostream>
#include <fstream>
using namespace std;

GLenum g_tex;
int g_res_m = 64;//128;//64;
int g_res_n = 64;//128;//128;
int g_res_l = 128;//128;//128;
int g_LastX = -1;
int g_LastY = -1;
float g_CamPos[] = {3.f, 0.f, 0.f};
float g_CamAngle[] = {0, 0};
float g_CamDistance = 3.f;
float g_CamRight[] = {0, 0, -1};
float g_CamUp[] = {0, 1, 0};
float g_CamDir[] = {-1, 0, 0};
bool g_LMBPressed = false;
bool g_RMBPressed = false;
bool g_MMBPressed = false;
float OneSec = 0;
float framesPerSec = 0;
int framesRendered = 0;
float g_sourcePos[] = {0, 0, 3};
float g_densityMultiplier = 1.f;
float g_timeStep = 5;

float g_LightPosRad = 3.f;
float g_Light_Phi = 20.04f;
float g_Light_Alp = 1.04f;
const float C_PI = 3.1415926535897932384626433832795f;
BOOL g_isDynamicLight = false;

ifstream camerafile;

CSmoke3DFlatWin::CSmoke3DFlatWin(int w, int h):glutWindow(w,h)
{
	g_show_HUD = true;

	//read camera positions
	camerafile.open("camera.txt", ios::in);
	m_numCameraPos = 0;
	while (!camerafile.eof())
	{
		camerafile>>m_CameraPos[m_numCameraPos].x>>m_CameraPos[m_numCameraPos].y>>m_CameraPos[m_numCameraPos].z;
		m_numCameraPos++;
	}
	m_CameraPosId = 0;
	camerafile.close();
	OnInit();
}

CSmoke3DFlatWin::~CSmoke3DFlatWin(void)
{
	//camerafile.close();
}

void CSmoke3DFlatWin::OnInit(void)
{
	//Init extensions
	m_glInfo.Init();

	//Set camera matrices, clear color etc.
	glViewport(0, 0, g_width, g_height);
	glClearColor(0, 0, 0, 0);
	glClearDepth(1.f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45, (float)g_width / (float)g_height, 0.01f, 30);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(g_CamPos[0], g_CamPos[1], g_CamPos[2], 0, 0, 0, 0, 1, 0);

	//Create fluid simulator
	float w = 1;
	float h = (float) g_res_n / (float) g_res_m;
	float d = (float) g_res_l / (float) g_res_m;
	fluidSimulator = new FluidGridSimulator(g_res_m, g_res_n, g_res_l, w, h, d);

	//create external sources and tell the simulator to use it in each frame
	//addAutoSources();
	//fluidSimulator->setLockExternalForces(true);
}

void CSmoke3DFlatWin::OnMouseMove(int x, int y, int mod)
{
	//get cursor movement since last event (dx, dy in pixels)
	int dx;
	int dy;
	if (g_LMBPressed || g_RMBPressed || g_MMBPressed)
	{
		if (g_LastX == -1)
		{
			g_LastX = x;
			g_LastY = y;
			return;
		}
		else
		{
			dx = x - g_LastX;
			dy = y - g_LastY;
		}
	}

	//move smoke smoke
	if (mod == GLUT_ACTIVE_CTRL || mod == GLUT_ACTIVE_SHIFT)
	{
		//printf("mod key ctrl\n");
		fluidSimulator->setWindSource(0, dx*0.1f, dy*0.1f, mod);
		g_LastX = x;
		g_LastY = y;
		return;
	}

	if (mod == GLUT_ACTIVE_SHIFT)
	{
		printf("mod key shift\n");
		return;
	}

	//zoom
	if (g_RMBPressed)
	{
		g_CamDistance -= (float)dx / 500.f;
		if (g_CamDistance < 0) g_CamDistance = 0;
		g_CamPos[0] = cos(g_CamAngle[0]) * cos(g_CamAngle[1]) * g_CamDistance;
		g_CamPos[2] = sin(g_CamAngle[0]) * cos(g_CamAngle[1]) * g_CamDistance;
		g_CamPos[1] = sin(g_CamAngle[1]) * g_CamDistance;
		g_LastX = x;
		g_LastY = y;
		fluidSimulator->setCameraChanged(true);
		return;
	}

	//rotate camera
	if (g_LMBPressed)
	{
		float dCamAngle1 = (float) dx / 500.f;
		float dCamAngle2 = (float) dy / 500.f;

		g_CamAngle[0] += dCamAngle1;
		g_CamAngle[1] += dCamAngle2;
		if (g_CamAngle[1] > 3.14 / 2.f) g_CamAngle[1] = 3.14 / 2.f;
		if (g_CamAngle[1] < -3.14 / 2.f) g_CamAngle[1] = -3.14 / 2.f;

		g_CamPos[0] = cos(g_CamAngle[0]) * cos(g_CamAngle[1]) * g_CamDistance;
		g_CamPos[2] = sin(g_CamAngle[0]) * cos(g_CamAngle[1]) * g_CamDistance;
		g_CamPos[1] = sin(g_CamAngle[1]) * g_CamDistance;
		g_LastX = x;
		g_LastY = y;

		float lengthCPos = sqrt(g_CamPos[0] * g_CamPos[0] + g_CamPos[1] * g_CamPos[1] + g_CamPos[2] * g_CamPos[2]);
		for(int i = 0; i < 3; i++)
			g_CamDir[i] = -g_CamPos[i] / lengthCPos;

		g_CamRight[0] = -g_CamDir[2];
		g_CamRight[1] = 0;
		g_CamRight[2] = g_CamDir[0];

		g_CamUp[0] = g_CamDir[1] * g_CamRight[2] - g_CamDir[2] * g_CamRight[1];
		g_CamUp[1] = g_CamDir[0] * g_CamRight[2] - g_CamDir[2] * g_CamRight[0];
		g_CamUp[2] = g_CamDir[0] * g_CamRight[1] - g_CamDir[1] * g_CamRight[0];
		fluidSimulator->setCameraChanged(true);
	}
}

void CSmoke3DFlatWin::OnMouseDown(int button, int x, int y)
{
	switch(button)
	{
	case GLUT_LEFT_BUTTON:
		g_LMBPressed = true;
		break;
	case GLUT_RIGHT_BUTTON:
		g_RMBPressed = true;
		break;
	case GLUT_MIDDLE_BUTTON:
		g_MMBPressed = true;
		break;
	}
}

void CSmoke3DFlatWin::OnMouseUp(int button, int x, int y)
{
	g_LastX = g_LastY = -1;
	switch(button)
	{
	case GLUT_LEFT_BUTTON:
		g_LMBPressed = false;
		break;
	case GLUT_RIGHT_BUTTON:
		g_RMBPressed = false;
		break;
	case GLUT_MIDDLE_BUTTON:
		g_MMBPressed = false;
		break;
	}
}

//   String rendering helper function (uses glutBitmapCharacter)(very slow)
//   font: font to use, e.g., GLUT_BITMAP_HELVETICA_10
//   r, g, b: text colour
//   x, y: text position in window: range [0,0] (bottom left of window) to [1,1] (top right of window). 
void CSmoke3DFlatWin::drawText(char *str, void *font, GLclampf r, GLclampf g, GLclampf b,
			  GLfloat x, GLfloat y) {

				  char *ch;

				  glMatrixMode(GL_PROJECTION);
				  glPushMatrix();
				  glLoadIdentity();
				  gluOrtho2D(0.0, 1.0, 0.0, 1.0);
				  glMatrixMode(GL_MODELVIEW);
				  glPushMatrix();
				  glLoadIdentity();
				  glPushAttrib(GL_COLOR_BUFFER_BIT);       
				  glColor4f(r, g, b,1);
				  glRasterPos3f(x, y, 0.0);
				  for(ch= str; *ch; ch++) {
					  glutBitmapCharacter(font, (int)*ch);
				  }
				  glPopAttrib();
				  glPopMatrix();
				  glMatrixMode(GL_PROJECTION);
				  glPopMatrix(); 
}

void CSmoke3DFlatWin::addAutoSources()
{
	g_sourcePos[0] = g_res_m / 2;
	g_sourcePos[1] = g_res_n / 2;
	float forceDir[] = {0, 0, 200};
	float sourceRadius = g_res_m / 20;
	float density = g_densityMultiplier;
	float temp = 100.f;
	forceDir[2] *= fluidSimulator->getDt();
	fluidSimulator->addExternalSpotSource(g_sourcePos, forceDir, sourceRadius, sourceRadius, sourceRadius,
		0.f, density, temp);
}

void CSmoke3DFlatWin::OnPreDisplay()
{

}

void CSmoke3DFlatWin::OnRender()
{
	//calculate frame time and FPS
	static int lastTime = timeGetTime();
	int currentTime = timeGetTime();

	int timeElapsed = currentTime - lastTime;
	OneSec += (float) timeElapsed / 1000.f;
	framesRendered++;

	if (OneSec >= 1.f)
	{
		framesPerSec = framesRendered - 2.f + OneSec;
		OneSec = 0;
		framesRendered = 0;
	}

	//Set bounding conditions for pressure and velocity and do one simulation step in the fluid
	fluidSimulator->setVelocityBoundScale(-1);
	fluidSimulator->setPressureBoundScale(1);

	if (!fluidSimulator->getPause())
		fluidSimulator->update();
	lastTime = currentTime;

	//Set camera matrices, clear color etc.
	glViewport(0, 0, g_width, g_height);
	glClearColor(0, 0, 0, 0);
	glClearDepth(1.f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45, (float)g_width / (float)g_height, 0.01f, 30);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	//gluLookAt(m_CameraPos[m_CameraPosId].x, m_CameraPos[m_CameraPosId].y, 
	//	m_CameraPos[m_CameraPosId].z, 0, 0, 0, 0, 1, 0);
	//if (m_CameraPosId < m_numCameraPos - 2) 
	//{
	//	m_CameraPosId++;
	//	if (m_CameraPos[m_CameraPosId-1].x != m_CameraPos[m_CameraPosId].x ||
	//		m_CameraPos[m_CameraPosId-1].y != m_CameraPos[m_CameraPosId].y ||
	//		m_CameraPos[m_CameraPosId-1].z != m_CameraPos[m_CameraPosId].z)
	//		fluidSimulator->setCameraChanged(true);
	//}
	//g_CamPos[0] = 2.08f;
	//g_CamPos[1] = -0.69f;
	//g_CamPos[2] = 2.04f;


	gluLookAt(g_CamPos[0], g_CamPos[1], g_CamPos[2], 0, 0, 0, 0, 1, 0);
	
	//camerafile<<g_CamPos[0]<<" "<<g_CamPos[1]<<" "<<g_CamPos[2]<<"\n";

	//compute new light source position
	if (g_isDynamicLight)
		g_Light_Alp += 0.01f;
	fluidSimulator->setLightSource(g_LightPosRad * sin(g_Light_Phi) * cos(g_Light_Alp),
									g_LightPosRad * cos(g_Light_Phi),
									g_LightPosRad * sin(g_Light_Phi) * sin(g_Light_Alp)
									);

	//render the fluid
	glDepthMask(false);
	fluidSimulator->render(g_width, g_height);
	glDepthMask(true);

	//Display HUD
	glViewport(0, 0, g_width, g_height);
	char line[200];
	sprintf(line, "%.2f FPS", framesPerSec);
	float linewidth = 1.f / (float)g_width * 14;
	float up = 1.f;
#define WRITELINE up -= linewidth; drawText(line, GLUT_BITMAP_HELVETICA_12, 0.5, 1, 0, 0.01, up);
	WRITELINE
	if (g_show_HUD)
	{
		up -= linewidth;
		sprintf(line, "Grid Resolution: %i x %i x %i", g_res_m, g_res_n, g_res_l);
		WRITELINE
			up -= linewidth;
		if(fluidSimulator->getEarlyZCull())
			sprintf(line, "[X] Early Z Culling (\'Z\')");
		else
			sprintf(line, "[  ] Early Z Culling (\'Z\')");
		WRITELINE
			if(fluidSimulator->getRenderBoundingCube())
				sprintf(line, "[X] Show Bounding Cube (\'C\')");
			else
				sprintf(line, "[  ] Show Bounding Cube (\'C\')");
		WRITELINE
			if(fluidSimulator->getVorticityConfinement())
				sprintf(line, "[X] Vorticity Confinement (\'W\')");
			else
				sprintf(line, "[  ] Vorticity Confinement (\'W\')");
		WRITELINE
		up -= linewidth;
		sprintf(line, "%i:  Iteration count (\'+\'/\'-\')", fluidSimulator->getIterationCount());
		WRITELINE
			sprintf(line, "%i:  render Quality - Number of slices (0: automatic) (\'K\'/\'L\')", fluidSimulator->getRenderSliceCount());
		WRITELINE
			sprintf(line, "%.1f:  Simulation Time Step (ms)(0:real-time)(\'N\'/\'M\')", g_timeStep);
		WRITELINE
			sprintf(line, "%.1f:  Viscosity (\'H\'/\'J\')", fluidSimulator->getViscosity());
		WRITELINE
			sprintf(line, "%.1f:  Density Scale (\'<\'/\'>\')", g_densityMultiplier);
		WRITELINE
		up -= linewidth;
		sprintf(line, "Display Mode:  %s (\'Q\')", fluidSimulator->getDisplayMode());
		WRITELINE
			if (fluidSimulator->getBuoyancyForces())
				sprintf(line,"[X] Buoyancy Forces (\'B\')");
			else
				sprintf(line,"[  ] Buoyancy Forces (\'B\')");
		WRITELINE
			if (fluidSimulator->getDensityGradient())
				sprintf(line,"[X] Compute density gradients (\'G\')");
			else
				sprintf(line,"[  ] Compute density gradients (\'G\')");
		WRITELINE
		up -= linewidth;
		sprintf(line, "Reset Density, Velocity, Temperature (\'R\')");
		WRITELINE
			if (fluidSimulator->getPause())
				sprintf(line,"[X] Pause (\'P\')");
			else
				sprintf(line,"[  ] Pause (\'P\')");
		WRITELINE
			sprintf(line, "%.5f: Strokes re-usage distance threshold (\'[\'/\']\')", fluidSimulator->getDisThres());
		WRITELINE
			up -= linewidth;
		sprintf(line, "Left Button + Drag Mouse: Move Camera");
		WRITELINE
			sprintf(line, "Right Button + Drag Mouse: Zoom Camera");
		WRITELINE
			sprintf(line, "SPACE: Show/Hide HUD");
		WRITELINE
	}

	glutSwapBuffers(); //swap buffers
}

void CSmoke3DFlatWin::OnIdle(void)
{
	glutPostRedisplay();
}

void CSmoke3DFlatWin::OnKeyDown(int nKey, char cAscii)
{

}

void CSmoke3DFlatWin::OnKeyUp(int nKey, char c)
{
	if(c == 27)
		exit(0);
	else if(c == 'q')
		fluidSimulator->switchDisplayMode();
	else if(c == 'a')
		fluidSimulator->switchStrokeTexture();
	else if(c == 'r')
	{
		fluidSimulator->resetDensity();
		fluidSimulator->resetVelocity();
		fluidSimulator->resetTemperature();
	}
	else if(c == 'v')
		fluidSimulator->is2Dsmoothed = !fluidSimulator->is2Dsmoothed;
	else if(c == 'w')
		//fluidSimulator->setVorticityConfinement(!fluidSimulator->getVorticityConfinement());
		fluidSimulator->switchNPRStyle();
	else if(c == 'z')
	{
		//fluidSimulator->setEarlyZCull(!fluidSimulator->getEarlyZCull());
		printf("eye position: %f %f %f\n", g_CamPos[0], g_CamPos[1], g_CamPos[2]);
	}
	else if(c == '=')
	{
		fluidSimulator->recsize++;
		printf("recsize : %d\n", fluidSimulator->recsize);
	}
	else if(c == '-')
	{
		if (fluidSimulator->recsize > 1)
			fluidSimulator->recsize--;
		printf("recsize : %d\n", fluidSimulator->recsize);
	}
	else if(c == ' ')
		g_show_HUD = !g_show_HUD;
	else if(c == 'n')
	{g_timeStep -= 1; if(g_timeStep < 0)g_timeStep = 0;}
	else if(c == 'm')
		g_timeStep += 1;
	else if(c == 'c')
		fluidSimulator->setRenderBoundingCube(!fluidSimulator->getRenderBoundingCube());
	else if(c == 'h')
		fluidSimulator->setViscosity(fluidSimulator->getViscosity() - 0.1);
	else if(c == 'j')
		fluidSimulator->setViscosity(fluidSimulator->getViscosity() + 0.1);
	else if(c == 'k')
		fluidSimulator->setRenderSliceCount(fluidSimulator->getRenderSliceCount() - 1);
	else if(c == 'l')
		fluidSimulator->setRenderSliceCount(fluidSimulator->getRenderSliceCount() + 1);
	else if(c == 'b')
		fluidSimulator->setBuoyancyForces(!fluidSimulator->getBuoyancyForces());
	else if (c == 'g')
	{
		fluidSimulator->switchIntensityMode();
		printf("Intensity mode: %d\n", fluidSimulator->intensitymode);
		//fluidSimulator->setDensityGradient(!fluidSimulator->getDensityGradient());
	}
	else if (c == 'p')
		fluidSimulator->setPause(!fluidSimulator->getPause());
	else if (c == '[')
	{
		if (fluidSimulator->nSmooth > 1)
			fluidSimulator->nSmooth--;
		fluidSimulator->smoothingSmokeData();
		printf("nsmooth: %d\n", fluidSimulator->nSmooth);
	}
	else if (c == ']')
	{
		fluidSimulator->nSmooth++;
		fluidSimulator->smoothingSmokeData();
		printf("nsmooth: %d\n", fluidSimulator->nSmooth);
	}
	else if (c == '8')
		g_isDynamicLight = !g_isDynamicLight;
	else if (nKey == 104)
	{
		fluidSimulator->dsampleX += 1;
		fluidSimulator->dsampleY += 1;
	}
	else if (nKey == 105)
	{
		if (fluidSimulator->dsampleX > 1)
			fluidSimulator->dsampleX -= 1;
		if (fluidSimulator->dsampleY > 1)
			fluidSimulator->dsampleY -= 1;
	}
	else if (nKey == 101)
	{
		fluidSimulator->m_contourtexsize++;
	}
	else if (nKey == 103)
	{
		if (fluidSimulator->m_contourtexsize > 1)
			fluidSimulator->m_contourtexsize--;
	}
	else if (c == 'u')
	{
		if (fluidSimulator->m_sampleRatio >= 2)
			fluidSimulator->m_sampleRatio -= 1;
		else
			fluidSimulator->m_sampleRatio -= 0.01f;
		fluidSimulator->genSamplingList();
		printf("sample_ratio (Wang tiles): %f\n", fluidSimulator->m_sampleRatio);
	}
	else if (c == 'i')
	{
		if (fluidSimulator->m_sampleRatio > 1)
			fluidSimulator->m_sampleRatio += 1;
		else
			fluidSimulator->m_sampleRatio += 0.1f;
		fluidSimulator->genSamplingList();
	}
	else if (c == '1')
	{
		//if (fluidSimulator->nSmooth > 1)
		//	fluidSimulator->nSmooth--;
		if (fluidSimulator->gausssig > 1)
			fluidSimulator->gausssig-=1.f;
		printf("Gaussig: %d\n", fluidSimulator->gausssig);
	}
	else if (c == '2')
	{
		//fluidSimulator->nSmooth++;
		fluidSimulator->gausssig+=1.f;
		printf("Gaussig: %d\n", fluidSimulator->gausssig);
	}
	else if (c == '3')
	{
		if (fluidSimulator->detectMode == 1.f) fluidSimulator->detectMode = 0.f;
		else fluidSimulator->detectMode = 1.f;
		printf("detect mode: %d\n", fluidSimulator->detectMode);
	}
	else if (c == '9')
	{
		//fluidSimulator->switchPointDistributeAlg();
		fluidSimulator->innerStrokeType = (fluidSimulator->innerStrokeType+1)%3;
	}
	else if (c == '7')
	{
		fluidSimulator->switchEdgeDetectionMode();
	}
	else if (c == '6')
	{
		fluidSimulator->switchJoinEdgeMode();
		printf("Join mode: %d\n", fluidSimulator->joinmode);
	}
	else if (c == '5')
	{
		fluidSimulator->switchHatchMode();
		printf("Hatch mode: %d\n", fluidSimulator->hatchmode);
	}
	else if (nKey == 107)
	{
		fluidSimulator->intensityThres -= 0.001f;
		printf("Intensity threshold: %f\n", fluidSimulator->intensityThres);

	}
	else if (nKey == 106)
	{
		fluidSimulator->intensityThres += 0.001f;
		printf("Intensity threshold: %f\n", fluidSimulator->intensityThres);
	}
	else
	{
		//if(c == '.')
		//	g_densityMultiplier *= 1.2;
		//else if(c == ',')
		//	g_densityMultiplier /= 1.2;
		//fluidSimulator->clearExternalForces();
		//addAutoSources();

		if(c == '.')
		{
			//fluidSimulator->densityThres += 0.001f;
			fluidSimulator->m_contourthres += 0.01f;
			printf("Density threshold: %f\n", fluidSimulator->m_contourthres);
		}
		else if(c == ',')
		{
			//fluidSimulator->densityThres -= 0.001f;
			fluidSimulator->m_contourthres -= 0.01f;
			printf("Density threshold: %f\n", fluidSimulator->m_contourthres);
		}
	}
}

void CSmoke3DFlatWin::OnClose()
{
}

void CSmoke3DFlatWin::OnResize(int width, int height)
{
	g_width = width/2.f;
	g_height = height;
}